package com.common.base.widget

import android.content.Context
import android.os.Handler
import android.os.Message
import android.text.TextUtils
import android.util.AttributeSet
import android.widget.TextView
import com.common.base.R
import com.common.base.utils.MathUtil.addComma
import java.text.DecimalFormat
import java.util.concurrent.Executors


/**
 *
 * 作者：sosou
 *
 * 版本：1.0
 *
 * 创建日期：4/5/2023
 *
 * 描述：自带数字滚动动画的TextView
 *
 * 修订历史：
 *
 */
class NumberRollingView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) :
    TextView(context, attrs, defStyleAttr) {
    private var frameNum: Int= 30 // 总共跳跃的帧数，默认30跳
    private var textType: Int= 0// 内容的类型，默认是金钱类型
    private var useCommaFormat: Boolean = false// 是否使用每三位数字一个逗号的格式，让数字显得比较好看，默认使用
    private var runWhenChange: Boolean = true// 是否当内容有改变才使用动画,默认是
    private val threadPool = Executors.newFixedThreadPool(1) // 1个线程的线程池
    private val formatter = DecimalFormat("0.00") // 格式化金额，保留两位小数
    private var nowMoneyNum = 0.00 // 记录每帧增加后的金额数字
    private var finalMoneyNum = 0.0 // 目标金额数字（最终的金额数字）
    private var nowNum = 0//记录每帧增加后的数字
    private var finalNum = 0//目标数字（最终的数字）
    private var preStr: String? = null

    /**
     * @Description 帧数
     */
    fun setFrameNum(frameNum: Int) {
        this.frameNum = frameNum
    }

    /**
     * @Description 内容的格式
     */
    fun setTextType(textType: Int) {
        this.textType = textType
    }

    /**
     * @Description 是否设置每三位数字一个逗号
     */
    fun setUseCommaFormat(useCommaFormat: Boolean) {
        this.useCommaFormat = useCommaFormat
    }

    /**
     * @Description 是否当内容改变的时候使用动画，反之则不使用动画
     */
    fun setRunWhenChange(runWhenChange: Boolean) {
        this.runWhenChange = runWhenChange
    }

    /**
     * @Description 设置需要滚动的金钱(必须为正数)或整数(必须为正数)的字符串
     */
    fun setContent(str: String) {
        //如果是当内容改变的时候才执行滚动动画,判断内容是否有变化
        if (runWhenChange) {
            if (TextUtils.isEmpty(preStr)) {
                //如果上一次的str为空
                preStr = str
                useAnimByType(str)
                return
            }

            //如果上一次的str不为空,判断两次内容是否一致
            if (preStr == str) {
                //如果两次内容一致，则不做处理
                return
            }

            //如果两次内容不一致，记录最新的str
            preStr = str
        }
        useAnimByType(str)
    }

    private fun useAnimByType(str: String) {
        if (textType == MONEY_TYPE) {
            startMoneyAnim(str)
        } else {
            startNumAnim(str)
        }
    }

    /**
     * @Description 开始金额数字动画的方法
     */
    fun startMoneyAnim(moneyStr: String) {
        // 如果传入的数字已经格式化了，则将包含的符号去除
        val money = moneyStr.replace(",", "").replace("-", "")
        try {
            finalMoneyNum = money.toDouble()
            if (finalMoneyNum == 0.0) {
                // 如果传入的数字为0则直接使用setText()进行显示
                this@NumberRollingView.text = moneyStr
                return
            }
            nowMoneyNum = 0.0
            threadPool.execute {
                val msg = handler.obtainMessage()
                // 将传入的数字除以帧数，得到每帧间隔的大小
                val size = finalMoneyNum / frameNum
                msg.what = MONEY_TYPE
                // 如果每帧的间隔小于0.01，则设置间隔为0.01
                msg.obj = if (size < 0.01) 0.01 else size
                // 发送消息改变UI
                handler.sendMessage(msg)
            }
        } catch (e: NumberFormatException) {
            e.printStackTrace()
            //如果转换Double失败则直接用setText()
            this@NumberRollingView.text = moneyStr
        }
    }

    /**
     * @Description 开始数字动画的方法
     */
    fun startNumAnim(numStr: String) {
        // 如果传入的数字已经格式化了，则将包含的符号去除
        val num = numStr.replace(",", "").replace("-", "")
        try {
            finalNum = num.toInt()
            if (finalNum < frameNum) {
                // 如果传入的数字比帧数小，则直接使用setText()
                this@NumberRollingView.text = numStr
                return
            }
            // 默认从0开始动画
            nowNum = 0
            threadPool.execute {
                val msg = handler.obtainMessage()
                // 将传入的数字除以帧数，得到每帧间隔的大小
                val temp = finalNum / frameNum
                msg.what = NUM_TYPE
                msg.obj = temp
                // 发送消息改变UI
                handler.sendMessage(msg)
            }
        } catch (e: NumberFormatException) {
            e.printStackTrace()
            //如果转换Integer失败则直接用setText
            this@NumberRollingView.text = numStr
        }
    }

    private val handler: Handler = object : Handler() {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            when (msg.what) {
                MONEY_TYPE -> {
                    //保留两位小数的字符串
                    val str = formatter.format(nowMoneyNum).toString()

                    // 更新显示的内容
                    if (useCommaFormat) {
                        //使用每三位数字一个逗号格式的字符串
                        val formatStr: String = addComma(str, true)
                        this@NumberRollingView.text = formatStr
                    } else {
                        this@NumberRollingView.text = str
                    }

                    //记录当前每帧递增后的数字
                    nowMoneyNum += msg.obj as Double
                    if (nowMoneyNum < finalMoneyNum) {
                        //如果当前记录的金额数字小于目标金额数字，即还没达到目标金额数字，继续递增
                        val msg2 = this.obtainMessage()
                        msg2.what = MONEY_TYPE
                        msg2.obj = msg.obj
                        // 继续发送通知改变UI
                        sendMessage(msg2)
                    } else {
                        //已经达到目标的金额数字，显示最终的数字
                        if (useCommaFormat) {
                            this@NumberRollingView.setText(
                                addComma(
                                    formatter.format(
                                        finalMoneyNum
                                    ), true
                                )
                            )
                        } else {
                            this@NumberRollingView.text = formatter.format(finalMoneyNum)
                        }
                    }
                }

                NUM_TYPE -> {
                    // 更新显示的内容
                    if (useCommaFormat) {
                        //使用每三位数字一个逗号格式的字符串
                        val formatStr: String = addComma(nowNum.toString(), false)
                        this@NumberRollingView.text = formatStr
                    } else {
                        this@NumberRollingView.text = nowNum.toString()
                    }

                    //记录当前每帧递增后的数字
                    nowNum += msg.obj as Int
                    if (nowNum < finalNum) {
                        //如果当前记录的数字小于目标数字，即还没达到目标数字，继续递增
                        val msg2 = this.obtainMessage()
                        msg2.what = NUM_TYPE
                        msg2.obj = msg.obj
                        // 继续发送通知改变UI
                        sendMessage(msg2)
                    } else {
                        //已经达到目标的数字，显示最终的内容
                        if (useCommaFormat) {
                            this@NumberRollingView.setText(
                                addComma(
                                    finalNum.toString(),
                                    false
                                )
                            )
                        } else {
                            this@NumberRollingView.text = finalNum.toString()
                        }
                    }
                }
            }
        }
    }

    init {
        val ta = context.obtainStyledAttributes(attrs, R.styleable.NumberRollingView)
        frameNum = ta.getInt(R.styleable.NumberRollingView_frameNum, 30)
        textType = ta.getInt(R.styleable.NumberRollingView_textType, MONEY_TYPE)
        useCommaFormat = ta.getBoolean(R.styleable.NumberRollingView_use_comma_format, true)
        runWhenChange = ta.getBoolean(R.styleable.NumberRollingView_run_when_change, true)
    }

    companion object {
        private const val MONEY_TYPE = 0
        private const val NUM_TYPE = 1
    }
}